﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using KeePassLib;
using KeePass.UI;
using KeePassLib.Collections;
using System.Linq;
using KeePassLib.Events;

namespace KeePass.Forms
{
    public abstract partial class UserControlEditRights : UserControl, IDisposable
    {
        protected AlternativeUser altUser;
        protected PwDatabase db;
        protected List<TreeNode> allNodes = new List<TreeNode>();
        protected abstract bool CheckedStateToChangeToAfterUserListChanged { get; }
    
        protected void Construct(AlternativeUser alternativeUser, PwDatabase db, CustomTreeViewEx ctmMainForm)
        {
            InitializeComponent();
            this.altUser = alternativeUser;
            this.db = db;
            Copy(ctmMainForm, treeView);  
            treeView.CheckBoxes = true;
            labelClick.Visible = alternativeUser == null;

            treeView.AfterCheck -= treeView_AfterCheck;    
            if (alternativeUser != null)
                AddCheckboxes(treeView.Nodes[0]);
            treeView.AfterCheck += treeView_AfterCheck;                
            if (treeView.Nodes.Count > 0)
            {
                FillAllNodes(treeView.Nodes[0]);
                treeView.Nodes[0].Expand();
                treeView.BeginUpdate();
                AddEventHandlerRecursive(treeView.Nodes[0]);
                treeView.EndUpdate();
            }

            // Workaround known winows bug concerning bold font TreeNodes
            // 
            // This is a known Windows bug. The simple solution is just to append an extra space character at the end of your strings.
            // The space character will not be visible, but it will increase the number of pixels needed to draw the string,
            // so the entire string will be visible.
            // Source: http://stackoverflow.com/questions/2272493/c-sharp-winforms-bold-treeview-node-doesnt-show-whole-text
            if(treeView.Nodes.Count>0)
                treeView.Nodes[0].Text = treeView.Nodes[0].Text+ "    ";

        }

        protected abstract void CheckedStateChangedAction(PwGroup group);
        protected abstract void AddEventHandler(PwGroup group);

        protected void ChangeCheckedStateUserListChanged(object sender, UserAddedRemovedEventArgs e)
        {
            if (e.AlternativeUser == this.altUser)
            {
                treeView.AfterCheck -= treeView_AfterCheck;
                TreeNode foundNode = allNodes.Where(tn => ((PwGroup)tn.Tag).Uuid.EqualsValue(((PwGroup)sender).Uuid)).Single();
                foundNode.Checked = CheckedStateToChangeToAfterUserListChanged;
                CheckedStateChangedAction((PwGroup)foundNode.Tag);
                treeView.AfterCheck += treeView_AfterCheck;
            }
        }

        private void AddEventHandlerRecursive(TreeNode n)
        {
            AddEventHandler(((PwGroup)n.Tag));
            foreach (TreeNode tn in n.Nodes)
                AddEventHandlerRecursive(tn);
        }

        private void FillAllNodes(TreeNode node)
        {
            allNodes.Add(node);
            foreach (TreeNode child in node.Nodes)
                FillAllNodes(child);
        }

        private void AddCheckboxes(TreeNode node)
        {
            node.Checked = UsersContains(node, this.altUser);
            foreach (TreeNode tn in node.Nodes)
                AddCheckboxes(tn);
        }

        private void Copy(TreeView treeview1, TreeView treeview2)
        {
            if (altUser != null)
            {
                TreeNode root=(TreeNode)treeview1.Nodes[0].Clone();
                treeview2.Nodes.Add(root);       
            }
        }

        protected abstract bool UsersContains(TreeNode t,AlternativeUser au);
        protected abstract void AddUserToList(TreeNode t,AlternativeUser au);

        private void AddUser(TreeNode t, AlternativeUser au)
        { 
                AddUserToList(t,au);
        }

        protected abstract void RemoveUserFromList(TreeNode t, AlternativeUser au);

        private void RemoveUser(TreeNode t, AlternativeUser au)
        { 
                RemoveUserFromList(t,au);
        }       

        private void CheckChilds(TreeNode tn,bool isChecked)
        {
            tn.Checked=isChecked;
            if (tn.Checked)
                AddUser(tn, altUser);
            else
                RemoveUser(tn, altUser);

            foreach (TreeNode node in (tn.Nodes))
                CheckChilds(node,isChecked);
        }
        private void CheckParent(TreeNode tn)
        {
            if (tn.Parent != null)
            {
                int checkedNodesCounter = 0;
                foreach (TreeNode node in tn.Parent.Nodes)
                    if (node.Checked)
                        checkedNodesCounter++;
                if (tn.Parent.Checked = checkedNodesCounter == tn.Parent.Nodes.Count)
                    AddUser(tn.Parent, altUser);
                else RemoveUser(tn.Parent, altUser);
                CheckParent(tn.Parent);
            }
        }

        private void treeView_AfterCheck(object sender, TreeViewEventArgs e)
        {
            treeView.AfterCheck -= treeView_AfterCheck;
            treeView.BeginUpdate();
            CheckParent(e.Node);
            CheckChilds(e.Node,e.Node.Checked);
            treeView.EndUpdate();
            treeView.AfterCheck += treeView_AfterCheck;     
        }
    } 
}
